package com.hmsg.health.security;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.hmsg.health.bean.domain.UserDo;
import com.hmsg.health.config.Constants;
import com.hmsg.health.service.UserService;
import com.hmsg.health.service.admin.AdminUserService;
import com.hmsg.health.utils.ApiException;
import com.hmsg.health.utils.CodecUtil;
import com.hmsg.health.utils.ErrorCode;
import com.hmsg.health.utils.StringUtil;


/**
 * 用于检查 token的拦截器
 *
 * @author Lujq
 * @since 1.0.0
 */
@Component
public class SecurityInterceptor extends HandlerInterceptorAdapter {
	private Logger logger = LoggerFactory.getLogger(SecurityInterceptor.class);

	private static final String AUTHENTICATION = "Authentication";
	private static final String CLIENT_CHANNEL = "X-Channel";
	private static final String CLIENT_USER = "X-User";

	@Autowired
	private TokenManager tokenManager;
	@Autowired
	private UserService userService;
	
	@Autowired
	private AdminUserService adminUserService;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		logger.info("方法拦截器url={}", request.getRequestURI() + "?" + StringUtil.convertMapToParameter(request.getParameterMap()));
		if ("/shuawujing/error".equals(request.getRequestURI())) {
			return true;
		}
		if(request.getRequestURI().indexOf("swagger")>0){
			return true;
		}  


		request.getHeader(CLIENT_CHANNEL);
		if (!CorsUtils.isPreFlightRequest(request)) {
			// 从切点上获取目标方法:拦截H5的token,并处理验证
			HandlerMethod handlerMethod = null;
			try {
				handlerMethod = (HandlerMethod) handler;
			} catch (ClassCastException e) {
				return false;
			}
			Method method = handlerMethod.getMethod();
			// 开放API(如微信付款通知)，不拦截请求
			if (method.isAnnotationPresent(OpenApi.class)) {
				return true;
			}
			// 若目标API已过时，需要提醒客户端强制升级
			if (method.isAnnotationPresent(Deprecated.class)) {
				throw new ApiException(ErrorCode.API_DEPRECATED);
			}

			String channel = request.getHeader(CLIENT_CHANNEL);

			if (channel!=null&&channel.startsWith("APP-HEALTH")) {
				// APP-WANDA-1-1.0.0
				String[] appVersions = channel.split("-");
				// 在此处可以拦截到客户端版本号，可以根据客户端版本号扩展一些操作

			}

			boolean f = method.isAnnotationPresent(IgnoreSecurity.class);
			logger.debug("f：{}", f);
			// 若目标方法忽略了安全性检查，则直接调用目标方法
			UserDo userDo = null;
			if (!method.isAnnotationPresent(IgnoreSecurity.class)&&!"/shuawujing/error".equals(request.getRequestURI())&&!method.isAnnotationPresent(IgnoreAdminSecurity.class)) {

				userDo = validateAuthentication(request,response);
			}
			
			if (method.isAnnotationPresent(IgnoreAdminSecurity.class)&&!"/shuawujing/error".equals(request.getRequestURI())) {

				userDo = validateAuthentication(request,response);
			}


			return true;
		}else{
			return true;
		}

	}

	/**
	 * 验证授权信息
	 *
	 * @param request
	 */
	private UserDo validateAuthentication(HttpServletRequest request,HttpServletResponse response) {
		String authentication = request.getHeader(AUTHENTICATION);
		if (StringUtil.isEmpty(authentication)) {
			returnJson(response);
			throw new ApiException(ErrorCode.NOT_LOGIN);


		}
		authentication = CodecUtil.decodeBASE64(authentication);
		// token+请求资源URI+TIMESTAMP
		String[] tokens = authentication.split("&");
		String userId = request.getHeader(CLIENT_USER);
		UserDo user = userService.findOne(Integer.parseInt(userId));
		String channel = request.getHeader(CLIENT_CHANNEL);
		logger.info("====channel={},userId={},tokens={},{},{}",channel,userId,tokens[0],tokens[1],tokens[2]);
		String token_key = null;
		//判断渠道
		if (channel.startsWith("APP-HEALTH")) {
			//万达app
			token_key=Constants.TOKEN_APP_LET;
		}else if(channel.startsWith("WXAPP-MNYX")){
			//微信小程序
			token_key=Constants.TOKEN_APP_LET;
		}else if(channel.startsWith("WEB")){
			//微信小程序
			token_key=Constants.TOKEN_WEB;
		}

		// 检查 token 有效性
		if (!tokenManager.checkToken(token_key, user.getMobile(), tokens[0])) {

			returnJson(response);
			throw new ApiException(ErrorCode.TOKEN_ERROR);
		}

		// 验证时间戳
		/*if (Math.abs(System.currentTimeMillis() - Long.parseLong(tokens[1])) > 3 * DateUtil.ONE_MINUTE) {

			System.out.println(System.currentTimeMillis()- Long.parseLong(tokens[1]));
			// 时间戳错误
			throw new ApiException(ErrorCode.TIMESTAMP_ERROR);
		}*/
		// 验证签名
		String sign = CodecUtil.encryptMD5(tokens[0] + request.getRequestURI() + tokens[1]);
		if (!sign.equals(tokens[2])) {
			// 签名错误
			logger.error("签名异常,客户端sign={},参数={},{},{}", tokens[2], tokens[0], request.getRequestURI(), tokens[1]);
			logger.error("服务端sign={}",sign);
			returnJson(response);
			throw new ApiException(ErrorCode.SIGN_ERROR);
		}


		return user;
	}

	private void returnJson(HttpServletResponse response){
        PrintWriter writer = null;
       response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("returnCode", 9014);
            map.put("message", "登录状态失效,请重新登录");
            map.put("data", "");
            org.json.JSONObject json  = new org.json.JSONObject(map);
            writer.print(json);
        } catch (IOException e){
        	throw new ApiException(ErrorCode.TOKEN_ERROR);
        } finally {
            if(writer != null){
                writer.close();
            }
        }
    }

}
